using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Grammars.Core
{
    public class ProductionRule<T> : Rule<T>, IEnumerable<Def<T>>
        where T : Node, new()
    {
        internal List<Def<T>> Defs { get; set; }

        public int Count
        {
            get { return Defs.Count; }
        }

        internal ProductionRule()
        {
            Defs = new List<Def<T>>();
        }

        public static Def<T> operator %(Def<T> left, ProductionRule<T> right)
        {
            var listR = new ProductionList<T>();
            listR.List.Add(right);
            return left % listR;
        }

        public static ProductionRule<T> operator +(Def<T> left, ProductionRule<T> right)
        {
            right.Defs.Insert(0, left);
            return right;
        }

        public static ProductionRule<T> operator +(ProductionRule<T> left, Def<T> right)
        {
            left.Defs.Add(right);
            return left;
        }

        public static ProductionList<T> operator |(Def<T> left, ProductionRule<T> right)
        {
            var rule = new ProductionRule<T>();
            rule.Defs.Add(left);

            foreach (var sr in left.SemanticRules)
                rule.AddRule(sr);

            return rule | right;
        }

        public static ProductionList<T> operator |(ProductionRule<T> left, Def<T> right)
        {
            var rule = new ProductionRule<T>();
            rule.Defs.Add(right);

            foreach (var sr in right.SemanticRules)
                rule.AddRule(sr);

            return left | rule;
        }
        
        public static ProductionList<T> operator |(ProductionRule<T> left, ProductionRule<T> right)
        {
            var plist = new ProductionList<T>();
            plist.List.Add(left);
            plist.List.Add(right);
            return plist;
        }

        public ProductionRule<T> With(Action<T> rule)
        {
            if (rule == null)
                throw new ArgumentNullException("rule");

            AddRule(rule);
            return this;
        }

        public ProductionRule<T> With(Action<ParameterList<T>> rule)
        {
            if (rule == null)
                throw new ArgumentNullException("rule");

            AddRule(rule);
            return this;
        }

        public ProductionRule<T> If(Func<T, bool> rule)
        {
            AddPredicate(rule);
            return this;
        }

        public ProductionRule<T> If(Func<ParameterList<T>, bool> rule)
        {
            AddPredicate(rule);
            return this;
        }

        public ProductionRule<T> If(Func<bool> rule)
        {
            AddPredicate(rule);
            return this;
        }

        internal void ApplyRules(T leftHand, T[] rightHand)
        {
            var nodes = new T[Defs.Count + 1];

            nodes[0] = leftHand;

            for (int i = 0; i < Defs.Count; i++)
            {
                Defs[i].Node = rightHand[i];
                nodes[i + 1] = Defs[i].Node;
            }

            ApplyRules(nodes);
        }

        public IEnumerator<Def<T>> GetEnumerator()
        {
            return Defs.GetEnumerator();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(Defs[0]);

            for (int i = 1; i < Defs.Count; i++)
                sb.Append(" " + Defs[i]);

            return sb.ToString();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public Def<T> this[int index]
        {
            get { return Defs[index]; }
        }

        public bool CheckPredicates()
        {
            throw new NotImplementedException();
        }
    }
}